﻿#include "Dispatcher.h"
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>

#define Max 1024  //管理文件描述符上限
struct SelectData
{
	fd_set readSet;
	fd_set writeSet;
};

static void* selectInit();
static int selectAdd(struct Channel* channel, struct EventLoop* evLoop);
static int selectRemove(struct Channel* channel, struct EventLoop* evLoop);
static int selectModify(struct Channel* channel, struct EventLoop* evLoop);
static int selectDispatch(struct EventLoop* evLoop, int timeout); // 单位: s
static int selectClear(struct EventLoop* evLoop);
static void setFdSet(struct Channel* channel, struct SelectData* data);
static void clearFdSet(struct Channel* channel, struct SelectData* data);

struct Dispatcher SelectDispatcher = {
	selectInit,
	selectAdd,
	selectRemove,
	selectModify,
	selectDispatch,
	selectClear
};

static void* selectInit()
{
	struct SelectData* data = (struct SelectData*)malloc(sizeof(struct SelectData));
	FD_ZERO(&data->readSet);//监控读集合清空
	FD_ZERO(&data->writeSet);//监控写集合清空

	return data;
}

static void setFdSet(struct Channel* channel, struct SelectData* data)
{
	if (channel->events & ReadEvent)
	{
		FD_SET(channel->fd, &data->readSet);//监控文件描述符存入 读集合
	}
	if (channel->events & WriteEvent)//监控文件描述符存入 写 集合
	{
		FD_SET(channel->fd, &data->writeSet);
	}
}
//清空监控集合
static void clearFdSet(struct Channel* channel, struct SelectData* data)
{
	if (channel->events & ReadEvent)
	{
		FD_CLR(channel->fd, &data->readSet);
	}
	if (channel->events & WriteEvent)
	{
		FD_CLR(channel->fd, &data->writeSet);
	}
}
//添加文件描述符
static int selectAdd(struct Channel* channel, struct EventLoop* evLoop)
{
	struct SelectData* data = (struct SelectData*)evLoop->dispatcherData;
	if (channel->fd >= Max)
	{
		return -1;
	}
	setFdSet(channel, data);
	return 0;
}
// 移除监控的文件描述符
static int selectRemove(struct Channel* channel, struct EventLoop* evLoop)
{
	struct SelectData* data = (struct SelectData*)evLoop->dispatcherData;
	clearFdSet(channel, data);
	// 通过 channel 释放对应的 TcpConnection 资源
	channel->destroyCallback(channel->arg);

	return 0;
}
//修改文件描述符 监控的事件（往集合里 写 或 删 ）
static int selectModify(struct Channel* channel, struct EventLoop* evLoop)
{
	struct SelectData* data = (struct SelectData*)evLoop->dispatcherData;
	setFdSet(channel, data);
	clearFdSet(channel, data);
	return 0;
}

//使用select监控，事件分发
static int selectDispatch(struct EventLoop* evLoop, int timeout)
{
	struct SelectData* data = (struct SelectData*)evLoop->dispatcherData;
	struct timeval val;//定义超时时长
	val.tv_sec = timeout;
	val.tv_usec = 0;//用不到，但要初始化（不初始化可能是随机数） sec+usec
	fd_set rdtmp = data->readSet;//使用 集合的 临时拷贝，select 每次调用都会更改传入的集合的内容（文件描述符 就绪set 不就绪clr）
	fd_set wrtmp = data->writeSet;

	int count = select(Max, &rdtmp, &wrtmp, NULL, &val);
	if (count == -1)
	{
		perror("select");
		exit(0);
	}
	for (int i = 0; i < Max; ++i)
	{
		if (FD_ISSET(i, &rdtmp))//查询 集合中 就绪的文件描述符i
		{
			eventActivate(evLoop, i, ReadEvent);
		}

		if (FD_ISSET(i, &wrtmp))
		{
			eventActivate(evLoop, i, WriteEvent);
		}
	}
	return 0;
}

//清理select
static int selectClear(struct EventLoop* evLoop)
{
	struct SelectData* data = (struct SelectData*)evLoop->dispatcherData;
	free(data);
	return 0;
}